/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.common.crypto.cipher;

import org.vacoor.nothing.common.util.Throwables;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;

/**
 * TODO 未完成！！封装 JCE (Java Cryptography Extension)
 *
 * @author vacoor
 */
public class Ciphers {
    public enum Algorithm {
        // 对称加密算法
        DES("DES"),
        AES("AES"),
        DES_EDE("DESede"),  // Triple DES
        RC2("RC2"), // RFC 2268
        RC4("RC4"),

        PBEWithMD5AndDES("PBEWithMD5AndDES"),
        PBEWithSHA1AndRC2_40("PBEWithSHA1AndRC2_40"),
        BLOWFISH("BLOWFISH"),
        ARCFOUR("ARCFOUR");


        private final String name;

        Algorithm(String name) {
            this.name = name;
        }

        public byte[] generateKey() {
            try {
                return KeyGenerator.getInstance(name).generateKey().getEncoded();
            } catch (NoSuchAlgorithmException e) {
                throw Throwables.unchecked(e);
            }
        }
    }

    // JDK5 支持的五种模式
    // Mode 需要设置加密参数 eg: IvParameterSpec
    public enum Mode {
        ECB, //Electronic Code Book, default
        CBC, //Cipher Block Chaining
        CFB, //Cipher Feedback
        OFB, //Output Feedback
        PCBC, //Propagating Cipher Block Chaining
    }

    // JDK5 支持的三种填充
    public enum Padding {
        NO_PADDING("NoPadding"), PKCS5_PADDING("PKCS5Padding"), ISO10226_PADDING("ISO10126Padding");

        private final String name;

        Padding(String name) {
            this.name = name;
        }
    }


    public static byte[] encrypt(String algorithm, byte[] keyBytes, byte[] iv, byte[] bytes) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
        return crypt(algorithm, keyBytes, iv, Cipher.ENCRYPT_MODE, bytes);
    }

    public static byte[] decrypt(String algorithm, byte[] keyBytes, byte[] iv, byte[] bytes) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
        return crypt(algorithm, keyBytes, iv, Cipher.DECRYPT_MODE, bytes);
    }

    private static byte[] crypt(String algorithm, byte[] keyBytes, byte[] iv, int jcaMode, byte[] bytes) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
        String transformation = algorithm;

        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, algorithm);
        // RSA JDK 只能传入 PKCS8EncodedKeySpec
        // PKCS8EncodedKeySpec rsa = new PKCS8EncodedKeySpec(keyBytes);
        // PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keyBytes);
        IvParameterSpec ivSpec = iv != null && iv.length > 1 ? new IvParameterSpec(iv) : null;

        Cipher cipher = Cipher.getInstance(transformation);

        initCipher(cipher, jcaMode, keySpec, ivSpec, null);

        return cipher.doFinal(bytes);
    }

    private static void initCipher(Cipher cipher, int jcaMode, Key key, AlgorithmParameterSpec spec, SecureRandom random) throws InvalidAlgorithmParameterException, InvalidKeyException {
        if (spec != null) {
            if (random != null) {
                cipher.init(jcaMode, key, spec, random);
            } else {
                cipher.init(jcaMode, key, spec);
            }
        } else {
            if (random != null) {
                cipher.init(jcaMode, key, random);
            } else {
                cipher.init(jcaMode, key);
            }
        }
    }
}
